import { _decorator, Component, Node, Quat, Vec3, math } from 'cc';
import { AUTO_AIM_INTERVAL_RANGE, AUTO_AIM_SCORE_WEIGHTS, TICK_RATES, AUTOAIM_REEVAL_MIN, AUTOAIM_REEVAL_MAX } from '../shared';
import { Clock } from '../core/Clock';
import { EnemyRegistry } from '../gameplay/EnemyRegistry';
import { GlobalEventBus } from '../core/EventBus';
import { hasLineOfSight } from '../math/LoS';
import type { WeaponStats } from '../shared';
import { AutoReloadSystem } from './AutoReloadSystem';
import { EntityIndex } from '../world/EntityIndex';

const { ccclass, property } = _decorator;

interface TargetScore {
  id: string;
  node: Node;
  score: number;
}

@ccclass('AutoAimSystem')
export class AutoAimSystem extends Component {
  @property(Clock)
  public clock: Clock | null = null;

  @property(AutoReloadSystem)
  public autoReload: AutoReloadSystem | null = null;

  @property(Node)
  public aimPivot: Node | null = null;

  private weaponStats: WeaponStats | undefined;
  private clockKey = '';
  private interval = AUTO_AIM_INTERVAL_RANGE.min;
  private accumulator = 0;
  private currentTargetId: string | null = null;
  private currentScore: number = 0;
  private lastLogAt = 0;
  private worldPos = new Vec3();
  private aimDir = new Vec3();
  private up = new Vec3(0, 1, 0);

  onEnable() {
    this.clockKey = `auto-aim-${this.node.uuid}`;
    this.ensureRegistered();
    this._pickNewInterval();
  }

  onDisable() {
    if (this.clock) {
      this.clock.unregister(this.clockKey);
    }
    this.unscheduleAllCallbacks();
  }

  /** 供安装器在依赖注入后显式绑定时钟并注册 */
  public bindClock(clock: Clock) {
    this.clock = clock;
    this.ensureRegistered();
  }

  private ensureRegistered() {
    if (!this.clock) return;
    // 防止重复注册
    (this.clock as any).unregister?.(this.clockKey);
    this.clock.register(this.clockKey, TICK_RATES.ai, (dt) => this.onAiTick(dt));
  }

  public getTargetId(): string | null {
    return this.currentTargetId;
  }

  private onAiTick(dt: number) {
    this.accumulator += dt;
    if (this.accumulator >= this.interval) {
      this.accumulator = 0;
      this._pickNewInterval();
      this.reselectTarget();
    }

    if (this.currentTargetId) {
      this.applyAim();
    }
  }

  private reselectTarget() {
    const stats = this.getWeaponStats();
    if (!stats) {
      return;
    }
    const origin = this.node.worldPosition;
    let best: TargetScore | null = null;
    let bestDist = Number.POSITIVE_INFINITY;
    const candidates = EntityIndex.listEnemies();
    for (const node of candidates) {
      if (!node || !node.isValid) continue;
      const comp = node.getComponent('EnemyDummy') as any;
      const id = comp?.id ?? node.uuid;
      const distance = Vec3.distance(origin, node.worldPosition);
      const distNorm = math.clamp(distance / stats.effectiveRange, 0, 1);
      const distScore = 1 - distNorm;
      const threatScore = 1 - distNorm; // 临时以距离代替
      const los = hasLineOfSight(origin, node.worldPosition) ? 1 : 0;
      const score =
        distScore * AUTO_AIM_SCORE_WEIGHTS.distance +
        threatScore * AUTO_AIM_SCORE_WEIGHTS.threat +
        los * AUTO_AIM_SCORE_WEIGHTS.lineOfSight;
      if (!best || score > best.score) {
        best = { id, node, score };
      }
      if (distance < bestDist) bestDist = distance;
    }

    // Fallback: if all scores are 0 (e.g., LoS not ready), choose nearest
    let newTargetId = best?.id ?? null;
    let newScore = best?.score ?? 0;
    if (best && best.score <= 0) {
      // find nearest
      let nearestId: string | null = null;
      let nearestDistance = Number.POSITIVE_INFINITY;
      for (const node of candidates) {
        if (!node || !node.isValid) continue;
        const d = Vec3.distance(origin, node.worldPosition);
        if (d < nearestDistance) { nearestDistance = d; nearestId = (node.getComponent('EnemyDummy') as any)?.id ?? node.uuid; }
      }
      if (nearestId) {
        newTargetId = nearestId;
        newScore = 0;
      }
    }

    // Throttled debug print
    const now = performance.now() / 1000;
    if (now - this.lastLogAt > 0.5) {
      const idForLog = newTargetId ?? 'None';
      if (typeof console !== 'undefined' && console.debug) {
        console.debug('[AAIM] candidates=', candidates.length, 'best=', idForLog, 'score=', newScore.toFixed(3));
      }
      this.lastLogAt = now;
    }
    if (newTargetId !== this.currentTargetId) {
      this.currentTargetId = newTargetId;
      this.currentScore = newScore;
      if (newTargetId) {
        GlobalEventBus.emit('hud/toast', { text: '切换目标', ms: 400 });
        if (typeof console !== 'undefined' && console.debug) {
          console.debug('[AAIM] target=', newTargetId, 'score=', newScore.toFixed(3));
        }
      }
    }
  }

  private getWeaponStats(): WeaponStats | undefined {
    const active = this.autoReload?.getActiveWeaponStats();
    if (active) {
      this.weaponStats = active;
    }
    return this.weaponStats;
  }

  private applyAim() {
    if (!this.aimPivot || !this.currentTargetId) {
      return;
    }
    const enemy = EnemyRegistry.get(this.currentTargetId);
    if (!enemy) {
      return;
    }
    const pivot = this.aimPivot;
    pivot.getWorldPosition(this.worldPos);
    const targetPos = enemy.node.worldPosition;
    Vec3.subtract(this.aimDir, targetPos, this.worldPos);
    this.aimDir.normalize();
    const rotation = new Quat();
    Quat.fromViewUp(rotation, this.aimDir, this.up);
    pivot.worldRotation = rotation;
  }

  private _pickNewInterval() {
    const min = AUTOAIM_REEVAL_MIN ?? AUTO_AIM_INTERVAL_RANGE.min;
    const max = AUTOAIM_REEVAL_MAX ?? AUTO_AIM_INTERVAL_RANGE.max;
    const range = Math.max(0.001, max - min);
    this.interval = min + Math.random() * range;
  }
}

